#include "stdafx.h"
#include "Shader.h"

#include "Texture.h"


CShader::CShader(CGcontext context)
: m_CgContext(context)
, m_technique(nullptr)
, m_pass(nullptr)
{
	assert(context);
}

CShader::~CShader()
{
	//	release shader resources
	for (TTechniquesMap::iterator t_it = m_techniques.begin(); t_it != m_techniques.end(); ++t_it)
	{
		TPassesList& passes = t_it->second.passes;
		for (TPassesList::iterator p_it = passes.begin(); p_it != passes.end(); ++p_it)
		{
			SAFE_RELEASE(p_it->vertex_shader);
			SAFE_RELEASE(p_it->vertex_shader);
		}
	}

	//	destroy effect
	cgDestroyEffect(m_CgEffect);
}

bool CShader::Load(const char* filename)
{
	m_CgEffect = cgCreateEffectFromFile(m_CgContext, filename, nullptr);
	if (!m_CgEffect)
		return false;

	CGtechnique cg_technique = cgGetFirstTechnique(m_CgEffect);
	while (cg_technique)
	{
		STechnique technique_desc;
		technique_desc.cg_technique = cg_technique;
		string technique_name = cgGetTechniqueName(cg_technique);

		CGpass cg_pass = cgGetFirstPass(cg_technique);
		while (cg_pass)
		{
			SPass pass_desc;
			pass_desc.cg_pass = cg_pass;

			//	get shader programs handles
			CGprogram vs = pass_desc.cg_vertex_program = cgGetPassProgram(cg_pass, CG_VERTEX_DOMAIN);
			CGprogram ps = pass_desc.cg_fragment_program = cgGetPassProgram(cg_pass, CG_FRAGMENT_DOMAIN);
			if (vs && ps)
			{
				//	compile shaders
				const char* vs_source = cgGetProgramString(vs, CG_COMPILED_PROGRAM);
				const char* ps_source = cgGetProgramString(ps, CG_COMPILED_PROGRAM);

				//	link shaders
				ID3DXBuffer *vs_code = nullptr, *ps_code = nullptr;
				HRESULT vs_link = D3DXAssembleShader(vs_source, strlen(vs_source), nullptr, nullptr, 0, &vs_code, nullptr);
				HRESULT ps_link = D3DXAssembleShader(ps_source, strlen(ps_source), nullptr, nullptr, 0, &ps_code, nullptr);
				if (SUCCEEDED(vs_link) && SUCCEEDED(ps_link))
				{
					//	create shaders
					HRESULT vs_create = g_pDevice->CreateVertexShader((ulong*)vs_code->GetBufferPointer(), &pass_desc.vertex_shader);
					HRESULT ps_create = g_pDevice->CreatePixelShader((ulong*)ps_code->GetBufferPointer(), &pass_desc.pixel_shader);
					if (SUCCEEDED(vs_create) && SUCCEEDED(ps_create))
					{
						//	save pass description
						technique_desc.passes.push_back(pass_desc);
					}

					//	release resources
					SAFE_RELEASE(vs_code);
					SAFE_RELEASE(ps_code);
				}
			}

			//	iterate to the next pass
			cg_pass = cgGetNextPass(cg_pass);
		}
		//	save technique description
		if (technique_desc.passes.size() > 0)
			m_techniques[technique_name] = technique_desc;

		cg_technique = cgGetNextTechnique(cg_technique);
	}

	return m_techniques.size() > 0;
}

uint CShader::BeginTechnique(const char* name)
{
	assert(!m_technique);

	TTechniquesMap::iterator it = m_techniques.find(name);
	if (it != m_techniques.end())
	{
		m_technique = &it->second;
		return m_technique->passes.size();
	}
	return 0;
}

void CShader::EndTechnique()
{
	assert(m_technique);
	m_technique = nullptr;
}

void CShader::BeginPass(uint index)
{
	assert(m_technique);
	assert(!m_pass);

	TPassesList& passes = m_technique->passes;
	assert(index < passes.size());

	m_pass = &passes[index];

	//	setup pass state
	cgSetPassState(m_pass->cg_pass);

	//	setup device
	g_pDevice->SetVertexShader(m_pass->vertex_shader);
	g_pDevice->SetPixelShader(m_pass->pixel_shader);
}

void CShader::EndPass()
{
	assert(m_pass);

	//	unbind shaders
	g_pDevice->SetVertexShader(nullptr);
	g_pDevice->SetPixelShader(nullptr);

	//	reset pass state
	cgResetPassState(m_pass->cg_pass);
	m_pass = nullptr;
}

void CShader::SetSamplerTexture(const char* sampler, ITexture* texture)
{
	assert(sampler);
	assert(texture);

	CTexture* pTexture = static_cast<CTexture*>(texture);
	IDirect3DTexture9* pDXTexture = *pTexture;


#define RAW_CG
#ifdef RAW_CG
	const SPass& pass = m_techniques.begin()->second.passes[0];
	CGparameter diffuse_sampler = cgGetNamedParameter(pass.cg_fragment_program, sampler);
	if (diffuse_sampler)
	{
		uint tex_register = cgGetParameterResourceIndex(diffuse_sampler);
		g_pDevice->SetTexture(tex_register, pDXTexture);
		cgSetSamplerState(diffuse_sampler);
	}
#else
	CGparameter diffuse_sampler = cgGetNamedEffectParameter(m_CgEffect, sampler);
	if (diffuse_sampler)
	{
		cgD3D9SetTextureParameter(diffuse_sampler, pDXTexture);
		cgSetSamplerState(diffuse_sampler);
	}
#endif
}